ひとりNavigation API Advent Calendar 13日目
https://gyazo.com/92c681bb3fbb35eb6d3639d760aacd03
これはひとりNavigation API Advent Calendarの13日目です。
History APIにはなかったintercept()はどのようにして生まれたかを探っていく
Multiple navigate handlers, and withholding the 'navigate' event? · Issue #89 · WICG/navigation-api · GitHub
このIssueが起点っぽい?
single-spaというライブラリがあり、これは複数のフレームワーク(React/Vue.js/Svelte)のルーターを同一ページで共存させるための仕組みをもっている
そのためにpopstateやpushStateをモンキーパッチして、子アプリがナビゲーションイベントを受け取るタイミングを制御している
Navigation API(App History API)の navigate イベントを 一時的に「保留」して、後続のハンドラに渡すタイミングを遅らせるようにしたい
例:アプリのマウント/アンマウントが完了するまで、他のルーターにイベントを渡したくない
提案された API アイデア
e.withhold(promise) や e.blockUntil(promise) のような API で イベント伝播を Promise 解決までブロックできるようにしたい
しかし、Navigation API の設計上、 ナビゲーション自体(URL 更新)は同期で行う必要があるため、実現は難しかった
この時点では preventDefault() でナビゲーションをキャンセルし、 single-spa の処理完了後に 同じ URL に再度 navigate し直すという方法が検討されていた
設計の具体的にはAppHistoryNavigateEvent.respondWith()というものが検討されていたがこれはPromiseのresolve値を返さないので、FetchEventのそれとは違っていた(由来自体はFetchEventのそれではあったみたい)
なので名前としてふさわしくないのではないか?という話があった
Allow multiple event handlers to call respondWith() · Issue #94 · WICG/navigation-api · GitHub
Navigation API の respondWith() は、複数のイベントハンドラが同時に呼び出すことができないという制約があり、後続の respondWith() 呼び出しは InvalidStateError になってしまう
この呼び出し方を変更していくことになったため、respondWith()だと意味が変わるため、名称変更が議論されていた
最終的に名称は transitionWhile() に変更された
この名称は、提供されたPromiseが保留中である間、アプリケーションが「遷移(transition)」状態に入ることを明示することを意図されている
transitionWhile doesn't quite work for scroll restoration · Issue #230 · WICG/navigation-api · GitHub
transitionWhile() はスクロール位置のキャプチャタイミングが不明確で、同期/非同期で挙動が破綻する問題がでた
NavigateEvent の変更(Chrome 105) | Blog | Chrome for Developers
例えば以下のような遅延処理があったときにURLはいつ更新されるべきか?についてが不明瞭ではあった
code:js
doSyncStuff();
event.transitionWhile((async () => {
await doAsyncStuff();
})());
Promiseが拒否された場合(ネットワークエラー等)、URLは移動先を示しているのにコンテンツは元のままという不整合が発生する
その結果スクロール状態の復活に不整合が起きることがある
transitionWhile doesn't quite work for scroll restoration · Issue #230 · WICG/navigation-api · GitHub
こうした問題を解決し、DOM 更新前にスクロール状態を確実に取得できるよう、handler関数を渡す新デザインが提案された
code:sample.js
event.transitionWhile(async (transition) => {
const response = await fetch(dataURL);
const data = await response.json();
transition.captureState();
await updateDOM(data);
});
最終的に transitionWhile() はCSSのtransition系のプロパティと混同されると懸念され intercept() に置き換える方向で合意して変更された
Replace transitionWhile()/canTransition with intercept()/canIntercept by domenic · Pull Request #235 · WICG/navigation-api · GitHub